package com.ichutney.oddsup;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Currency;
import java.util.Date;
import java.util.Formatter;
import java.util.LinkedList;
import java.util.Locale;
import java.util.Stack;

public class SandP {

	private Integer buyCount[] = { 0, 0, 0, 0, 0, 0 };
	private Integer sellCount[] = { 0, 0, 0, 0, 0, 0 };
	private Integer sellOnCloseCount = 0, coverOnCloseCount=0;
	private Float rangeOpen = Float.valueOf("0.0"), rangeClose = Float.valueOf("0.0"), rangeHigh = Float.MIN_VALUE, rangeLow = Float.MAX_VALUE, open, high, low, close;
	private Double buy[] = {0.0, 0.0}, buyTarget[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, buyLoss[] = {0.0, 0.0};
	private Double sell[] = {0.0, 0.0}, sellTarget[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, sellLoss[] = {0.0, 0.0};
	private Double total = 0.0, totalSell = 0.0, totalBuy = 0.0, totalLosses = 0.0, totalBuyLoss = 0.0, totalSellLoss = 0.0;
	private Integer maxSharesTraded=0, minSharesTraded=0;
	private Double maxProfitTraded=0.0, minProfitTraded=0.0;
	private Double maxLossTraded=0.0, minLossTraded=0.0;
	private NumberFormat nmft = NumberFormat.getInstance();
	private PrintWriter stringOut = new PrintWriter(System.out); 
	private String name = "shld";
	private Date fromDate, toDate;
	double commission = 4.95;
	boolean longFlag=true, shortFlag=true, actionIntervalFlag=false;
	double maxrisk = 25000.0;			
	int maxshares = 0;
	Double principal = 5000.0;
	

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the stringOut
	 */
	public void setStringOut(PrintWriter writer) {
		stringOut = writer;
	}

	/**
	 * @return the fromDate
	 */
	public Date getFromDate() {
		return fromDate;
	}

	/**
	 * @param fromDate the fromDate to set
	 */
	public void setFromDate(Date fromDate) {
		this.fromDate = fromDate;
	}

	/**
	 * @return the toDate
	 */
	public Date getToDate() {
		return toDate;
	}

	/**
	 * @param toDate the toDate to set
	 */
	public void setToDate(Date toDate) {
		this.toDate = toDate;
	}

	/**
	 * @return the commission
	 */
	public double getCommission() {
		return commission;
	}

	/**
	 * @param commission the commission to set
	 */
	public void setCommission(double commission) {
		this.commission = commission;
	}

	/**
	 * @return the longFlag
	 */
	public boolean isLongFlag() {
		return longFlag;
	}

	/**
	 * @param longFlag the longFlag to set
	 */
	public void setLongFlag(boolean longFlag) {
		this.longFlag = longFlag;
	}

	/**
	 * @return the shortFlag
	 */
	public boolean isShortFlag() {
		return shortFlag;
	}

	/**
	 * @param shortFlag the shortFlag to set
	 */
	public void setShortFlag(boolean shortFlag) {
		this.shortFlag = shortFlag;
	}

	/**
	 * @return the maxrisk
	 */
	public double getMaxrisk() {
		return maxrisk;
	}

	/**
	 * @param maxrisk the maxrisk to set
	 */
	public void setMaxrisk(double maxrisk) {
		this.maxrisk = maxrisk;
	}

	/**
	 * @return the maxshares
	 */
	public int getMaxshares() {
		return maxshares;
	}

	/**
	 * @param maxshares the maxshares to set
	 */
	public void setMaxshares(int maxshares) {
		this.maxshares = maxshares;
	}

	/**
	 * @return the principal
	 */
	public Double getPrincipal() {
		return principal;
	}

	/**
	 * @param principal the principal to set
	 */
	public void setPrincipal(Double principal) {
		this.principal = principal;
	}

	/**
	 * 
	 */
	public SandP() {
		super();
		nmft.setCurrency(Currency.getInstance(Locale.US));
		nmft.setMaximumFractionDigits(2);
		// TODO Auto-generated constructor stub
	}

	/**
	 * @param symbol
	 * @param fromDate
	 * @param toDate
	 * @return
	 * @throws MalformedURLException
	 * @throws IOException
	 */
	public BufferedReader reader(String symbol, Date fromDate, Date toDate) throws MalformedURLException, IOException
	{
		SimpleDateFormat fmt = new SimpleDateFormat("MMM+dd,+yyyy"); //Jun+21%2C+2011	//Jun+19%2C+2012
		String urlString = "http://www.google.com/finance/historical?q="+symbol+"&startdate="+fmt.format(fromDate)
				+"&enddate="+(toDate!=null?fmt.format(toDate):fmt.format(new Date()))+"&num=30&output=csv";
		//"http", proxy, Integer.parseInt(proxyPort)
		//URL url = new URL("http", "webproxy.int.westgroup.com", 80, urlString);
		URL url = new URL(urlString);
		
		BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
//		String inputLine;
//		while ((inputLine = in.readLine()) != null)
//			stringOut.println(inputLine);
//		in.close();
		return in;
	}
	
	/**
	 * @param shares
	 * @param maxshares
	 * @return
	 */
	private int getMaxShareCount(int shares, int maxshares){
		return (maxshares>0 && shares>maxshares)?maxshares:shares;
	}
	
	/**
	 * @param risk
	 * @param maxrisk
	 * @return
	 */
	private double getMaxRiskCapital(double risk, double maxrisk){
		return (maxrisk>0 && risk>maxrisk)?maxrisk:risk;
	}

	/**
	 * @param shares
	 * @param profit
	 * @param loss
	 */
	private void computeStats(int shares, double profit, double loss){
		//maxSharesTraded = 
	}
	
	/**
	 * 
	 */
	public void process()
	{
		try {
			Double currentTotal = principal;
			BufferedReader bfr;
			int shareCount; 
			double profit, loss;
			SimpleDateFormat sdf = new SimpleDateFormat("MM-dd-yyyy");			
			String fromDt = "01-01-2012";
			String toDt = "";	//"12-31-2011";
			File file = new File(name+fromDt+".csv");
			boolean writeFlag = false;
			stringOut.println("==================================================================================");
			stringOut.println("                           "+name.toUpperCase()+"     ");
			stringOut.println("----------------------------------------------------------------------------------");
			if (file.exists()) {
				bfr = new BufferedReader(new FileReader(file));
			} else {
				bfr = reader(name, (this.fromDate!=null?this.fromDate:sdf.parse(fromDt)), (this.toDate!=null?this.toDate:(toDt.length()>0?sdf.parse(toDt):null)));
				writeFlag = true;
			}
			String header = bfr.readLine();
			Integer count = 0, noTrades = 0;
			Stack<String> stack = new Stack<String>();
			while (bfr.ready()) {
				String row = bfr.readLine();
				stack.add(row);
			}
			while(stack.size()>0) {
				String row = stack.pop();
				String[] vals = row.split(",");
				open = Float.parseFloat(vals[1]);
				high = Float.parseFloat(vals[2]);
				low = Float.parseFloat(vals[3]);
				close = Float.parseFloat(vals[4]);

				printRange(vals[0]);
				// calculate
				if(count == 0) {
					rangeOpen = open;
				}else{
					rangeHigh = Math.max(rangeHigh, high);
					rangeLow = Math.min(rangeLow, low);
					rangeClose = close;					
				}
				if (count > 0) {
					// Buy Rules - Up Days
					//if (low < buy[0] && close > low && open < close && open >= low) {
					// Low is below the Buy point... UP Day Only (open is below close)
					if (open < close && low < buy[0] && longFlag) {
						if (buy[0] < high) {
							// Assume the stops are raised with each buy targets 
							// So if the highest buy target is not hit we calculate on the basis of sell on close
							double profit4Interval = 0;
							profit=0;
							shareCount = getMaxShareCount((int)(getMaxRiskCapital(currentTotal, maxrisk)/buy[0]), maxshares);
							if(buyTarget[buyTarget.length-1]<=close)
							{
								sellOnCloseCount++;
								profit4Interval = close - buy[0];
								profit = shareCount*profit4Interval;
								printBuys(shareCount, buy[0], close, buyLoss[0], currentTotal, profit);
							}else
							{
								for(int b=5; b>=0; b--)
								{
									if (buyTarget[b] <= high) {	
										profit4Interval = (buyTarget[b] - buy[0]); 
										buyCount[b]++;
										profit = shareCount*profit4Interval;
										printBuys(shareCount, buy[0], buyTarget[b], buyLoss[0], currentTotal, profit);
										break;
									}	
								}
							}
							if(profit4Interval>0)
							{
								total += profit4Interval;
								totalBuy+= profit4Interval;
								currentTotal += profit;
							}
						}
						// Stop Losses
						if(buy[0]<=high && buyTarget[0]>high)
						{
							if(close <= buyLoss[0]){
								totalLosses += Math.abs(buy[0]-close);
								totalBuyLoss += Math.abs(buy[0]-close);
								shareCount = getMaxShareCount((int)(getMaxRiskCapital(currentTotal, maxrisk)/buy[0]), maxshares);
								loss = (shareCount)*Math.abs(buy[0]-close);
								currentTotal -= loss;
								printBuyStopLoss(shareCount, buy[0], close, buyLoss[0], currentTotal, loss);
							}
							else if(low <= buyLoss[0]){
								totalLosses += Math.abs(buy[0]-buyLoss[0]);
								totalBuyLoss += Math.abs(buy[0]-buyLoss[0]);
								shareCount = getMaxShareCount((int)(getMaxRiskCapital(currentTotal, maxrisk)/buy[0]), maxshares);
								loss = (shareCount)*Math.abs(buy[0]-buyLoss[0]);
								currentTotal -= loss;
								printBuyStopLoss(shareCount, buy[0], buyLoss[0], buyLoss[0], currentTotal, loss);
							}
						}
					}
					// sell Rules - Down Days					
					//if (high > sell[0] && close < high && close < open && close <= high) {
					// High is above Sell Point.. DOWN Day Only (close is below open)
					if (close < open && high > sell[0] && shortFlag) {
						if (sell[0] > low ) {
							// Assume the stops are lowered with each cover targets 
							// So if the highest cover target is not hit we calculate on the basis of cover on close
							double profit4Interval = 0;
							profit=0;
							shareCount = getMaxShareCount((int)(getMaxRiskCapital(currentTotal, maxrisk)/sell[0]), maxshares);
							if(close<sellTarget[sellTarget.length-1])
							{
								coverOnCloseCount++;
								profit4Interval = Math.abs(sell[0] - close);
								profit = shareCount*profit4Interval;
								printSells(shareCount, sell[0], close, sellLoss[0], currentTotal, profit);
							}else
							{							
								for (int b = 5; b >= 0; b--) {
									if (sellTarget[b] >= low) {
										profit4Interval = Math.abs(sell[0] - sellTarget[b]);
										sellCount[b]++;
										profit = shareCount*profit4Interval;
										printSells(shareCount, sell[0], sellTarget[b], sellLoss[0], currentTotal, profit);
										break;
									}
								}
							}
							if(profit4Interval>0)
							{
								total += profit4Interval;
								totalSell += profit4Interval;
								currentTotal += profit;
							}							
						}
						// Stop Losses
						if(sell[0]>=low && sellTarget[0]<low)
						{
							if(close >= sellLoss[0]){
								totalLosses += Math.abs(close-sell[0]);
								totalSellLoss += Math.abs(close-sell[0]);
								shareCount = getMaxShareCount((int)(getMaxRiskCapital(currentTotal, maxrisk)/sell[0]), maxshares);
								loss = (shareCount)*Math.abs(close-sell[0]);
								currentTotal -= loss;
								printSellStopLoss(shareCount, sell[0], close, sellLoss[0], currentTotal, loss);
							}
							else if(high >= sellLoss[0]){
								totalLosses += Math.abs(sellLoss[0]-sell[0]);
								totalSellLoss += Math.abs(sellLoss[0]-sell[0]);
								shareCount = getMaxShareCount((int)(getMaxRiskCapital(currentTotal, maxrisk)/sell[0]), maxshares);
								loss = (shareCount)*Math.abs(sellLoss[0]-sell[0]);
								currentTotal -= loss;
								printSellStopLoss(shareCount, sell[0], sellLoss[0], sellLoss[0], currentTotal, loss);
							}
						}
					}
				}

				// calculate next day buy values
				buy[0] = close * 0.238 / 100.0 + close;
				buyTarget[0] = close * 0.618 / 100.0 + close;
				buyTarget[1] = close * 1.0 / 100.0 + close;
				buyTarget[2] = close * 1.618 / 100.0 + close;

				buyLoss[0] = Math.abs((buy[0]) * 0.38 / 100 - buy[0]);

				buy[1] = close * 0.38 / 100.0 + close;
				buyTarget[3] = close * 1.618 / 100.0 + close;
				buyTarget[4] = close * 2.618 / 100.0 + close;
				buyTarget[5] = close * 4.25 / 100.0 + close;

				buyLoss[1] = Math.abs((buy[1]) * 0.75 / 100 - buy[1]);

				// calculate next day sell values
				sell[0] = Math.abs(close * 0.238 / 100.0 - close);
				sellTarget[0] = Math.abs(close * 0.618 / 100.0 - close);
				sellTarget[1] = Math.abs(close * 1.0 / 100.0 - close);
				sellTarget[2] = Math.abs(close * 1.618 / 100.0 - close);

				sellLoss[0] = Math.abs((sell[0]) * 0.38 / 100 + sell[0]);

				sell[1] = Math.abs(close * 0.38 / 100.0 - close);
				sellTarget[3] = Math.abs(close * 1.618 / 100.0 - close);
				sellTarget[4] = Math.abs(close * 2.618 / 100.0 - close);
				sellTarget[5] = Math.abs(close * 4.25 / 100.0 - close);

				sellLoss[0] = Math.abs((sell[1]) * 0.75 / 100 + sell[1]);

				count++;
			}


			stringOut.println("\n\n\n");
			stringOut.println("===========================================================================");
			stringOut.println("Range Open: "+rangeOpen+" High: "+rangeHigh+" Low: "+rangeLow+" Close: "+rangeClose);
			stringOut.println("===========================================================================");
			
			if(longFlag){
				stringOut.println("=========================================");
				stringOut.println("========      BUYS      =================");
				stringOut.println("=========================================");
				int buyDays = 0;
				for (int j = 0; j < buyCount.length; j++) {
					stringOut.println("BuyTarget[" + j + "]  Count: "
							+ buyCount[j]);
					buyDays+=buyCount[j];
				}				
				stringOut.println("Sell On Close  Count: "+sellOnCloseCount);
				stringOut.println("=========================================");
				stringOut.println("Total Buys: "+ buyDays + " Buy Targets: "+nmft.format(totalBuy));
				stringOut.println("Total Buy Loss: "+ nmft.format(totalBuyLoss));
				stringOut.println("=========================================");
			}
			if(shortFlag){
				stringOut.println("=========================================");
				stringOut.println("========      SELLS      =================");
				stringOut.println("=========================================");
				int sellDays = 0;
				for (int j = 0; j < sellCount.length; j++) {
					stringOut.println("SellTarget[" + j + "]  Count: "
							+ sellCount[j]);
					sellDays+=sellCount[j];
				}				
				stringOut.println("Cover On Close  Count: "+coverOnCloseCount);
				stringOut.println("=========================================");
				stringOut.println("Total Sells: "+ sellDays + " Sell Targets: "+nmft.format(totalSell));
				stringOut.println("Total Sell Loss: "+ nmft.format(totalSellLoss));
				stringOut.println("=========================================");
			}
			
			stringOut.println("Total Days: " + count);
			stringOut.println("Total Profits: " + nmft.format(total));
			stringOut.println("Total Losses: "+ nmft.format(totalLosses));
			stringOut.println("Grand Total: "+ nmft.format(total-totalLosses));
			stringOut.println("====================================================================================");
			stringOut.println("Cash Value: "+nmft.format(currentTotal));
			stringOut.println("====================================================================================");
			
			// flush
			stringOut.flush();
		} catch (Exception ex) {
			ex.printStackTrace();
		}		
	}
	
	
	/**
	 * @param dt
	 */
	public void printRange(String dt)
	{
		stringOut.println("@@@@ "+dt+"||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||");
		stringOut.println("-----------------------------------------------------------------------------------");
		stringOut.printf("====> open: %.2f --- high: %.2f --- low: %.2f --- close: %.2f <====\n", open, high, low, close);
		for(int i=0; i<buyTarget.length; i++)
			stringOut.printf(" [+] buy["+i+"] => "+nmft.format(buyTarget[i]));
		for(int i=0; i<buyLoss.length; i++)
			stringOut.printf(" [-] buyLoss["+i+"] => "+nmft.format(buyLoss[i]));
		stringOut.printf("\n");
		for(int i=0; i<sellTarget.length; i++)
			stringOut.printf(" [+] sell["+i+"] => "+nmft.format(sellTarget[i]));
		for(int i=0; i<sellLoss.length; i++)
			stringOut.printf(" [-] sellLoss["+i+"] => "+nmft.format(sellLoss[i]));
		stringOut.printf("\n");
		stringOut.println("-----------------------------------------------------------------------------------\n");
	}
	
	/**
	 * @param shareCount
	 * @param buy
	 * @param sell
	 * @param sloss
	 * @param total
	 * @param profit
	 */
	public void printBuys(int shareCount, double buy, double sell, double sloss, double total, double profit)
	{
		stringOut.println("---------------------BUY BUY BUY BUY BUY-------------------------------------------");
		stringOut.printf("+++++ buy: %.2f **** sell: %.2f **** stop loss: %.2f +++++\n",buy, sell, sloss);
		stringOut.println("#### Shares: "+shareCount+"  $$$$ Cash Total $$$$: "+nmft.format(total)+ " ===> PROFIT: "+nmft.format(profit));
		stringOut.println("-----------------------------------------------------------------------------------\n");
	}

	/**
	 * @param shareCount
	 * @param sell
	 * @param cover
	 * @param sloss
	 * @param total
	 * @param profit
	 */
	public void printSells(int shareCount, double sell, double cover, double sloss, double total, double profit)
	{
		stringOut.println("----------------------SELL SELL SELL SELL-----------------------------------------");
		stringOut.printf("XXXXX sell: %.2f **** cover: %.2f **** stop loss: %.2f XXXXX\n",sell, cover, sloss);
		stringOut.println("#### Shares: "+shareCount+"  $$$$ Cash Total $$$$: "+nmft.format(total)+ " ===> PROFIT: "+nmft.format(profit));
		stringOut.println("-----------------------------------------------------------------------------------\n");
	}
	
	/**
	 * @param shareCount
	 * @param buy
	 * @param sell
	 * @param sloss
	 * @param total
	 * @param loss
	 */
	public void printBuyStopLoss(int shareCount, double buy, double sell, double sloss, double total, double loss)
	{
		stringOut.println("----------------------BUY LOSS BUY LOSS-------------------------------------------");
		stringOut.printf("[][]  buy: %.2f **** sell: %.2f **** stop loss: %.2f  [][]\n",buy, sell, sloss);
		stringOut.println("#### Shares: "+shareCount+"  $$$$ Cash Total $$$$: "+nmft.format(total)+ " ===> LOSS: "+nmft.format(loss));
		stringOut.println("-----------------------------------------------------------------------------------\n");
	}

	/**
	 * @param shareCount
	 * @param sell
	 * @param cover
	 * @param sloss
	 * @param total
	 * @param loss
	 */
	public void printSellStopLoss(int shareCount, double sell, double cover, double sloss, double total, double loss)
	{
		stringOut.println("----------------------SELL LOSS SELL LOSS------------------------------------------");
		stringOut.printf("##### sell: %.2f **** cover: %.2f **** stop loss: %.2f #####\n",sell, cover, sloss);
		stringOut.println("#### Shares: "+shareCount+"  $$$$ Cash Total $$$$: "+nmft.format(total)+ " ===> LOSS: "+nmft.format(loss));
		stringOut.println("-----------------------------------------------------------------------------------\n");
	}

	public static void main(String[] args) {
		SandP sp = new SandP();
		StringWriter str = new StringWriter();
		PrintWriter pwriter = new PrintWriter(str);
		sp.setStringOut(pwriter);
		sp.process();
		System.out.println(str.getBuffer().toString());
	}
}
